#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;

/*用于判断矩形是否是车牌的参数*/
#define ERROR 0.15f
#define ASPECT 3.1428f
#define AREA_MAX 10000
#define AREA_MIN 100
#define ANGLE 5
/*lab颜色空间的阈值*/
#define T1 50   //蓝色阈值
#define T2 137  //黄色阈值
#define T3 130  //绿色阈值
#define T4 165  //红色阈值，我是一个个试的

/*定义的车牌类，还没写完*/
class Plate : public RotatedRect
{
public:
	Plate();
	Plate(CvBox2D);
	void DrawPlate(IplImage*);

	float area;
	float far;
};

Plate::Plate(): RotatedRect()
{
	far = 0;
	area = 0;
}
Plate::Plate(CvBox2D B): RotatedRect(B)
{
	area = B.size.height * B.size.width;
	far = 0; //***************
}

void Plate::DrawPlate(IplImage* src) //画车牌矩形，写出函数就报错，以放弃，直接写在main函数里了
{
/*	Point2f vertices[4];
	points(vertices);
	for(int i = 0; i<4 ; i++)
		cvLine(&src,vertices[i],vertices[(i+1)%4],CvScalar(255,0,0),1);

	//rectangle((Mat)src,boundingRect(),CvScalar(255,0,0));
		CvPoint2D32f Rect_pts0[4];
	int npt = 4;
	cvBoxPoints(Rect, Rect_pts0);
				 CvPoint rect_pts[4],*pt = rect_pts;
					for (int rp=0; rp<4; rp++)
						 rect_pts[rp]= cvPointFrom32f(Rect_pts0[rp]);
				cvPolyLine(img, &pt, &npt, 1, 1, CV_RGB(255,0,0), 1);
*/
}

bool verify(CvBox2D box)//判断矩形是否是车牌，面积，宽高比，角度
{
	float area = box.size.height*box.size.width;

	if( area< AREA_MIN || area> AREA_MAX )
		return false;

	float r = (float)box.size.width/(float)box.size.height;
	if(r<1)
		r = (float)box.size.height/(float)box.size.width;

	if(r<(ASPECT-ASPECT*ERROR)||r>(ASPECT+ASPECT*ERROR))
		return false;

	if(-box.angle>ANGLE&&-box.angle<90-ANGLE)
		return false;
	return true;
}


int main(int argc, char* argv[])
{

	char InputFile[50];// =
	char OutputFile[50];// = "F:\\output\\test%d.bmp";
	for(int i = 0;i<5;i++)  //i为输入图像的数目
	{
		sprintf(InputFile,"/home/liuyang/Downloads/OPENCV_CODE/input/test%d.bmp",i);//图像存放目录，测试图片命名test<i>
		cout<<InputFile<<endl;
		IplImage *img,*img_L,*img_a,*img_b,*img_b_16,*img_b1;
		img = cvLoadImage(InputFile,1);  //输入图像

		img_L = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);//创建分通道
		img_a = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
		img_b = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
		img_b1 = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
		img_b_16 = cvCreateImage(cvGetSize(img),IPL_DEPTH_16S,1);

//		cvSmooth(img,img,CV_GAUSSIAN,3,3);//高斯模糊
		cvSmooth(img,img,CV_MEDIAN,3,3);//中值滤波模糊 3*3是核的大小 用高斯模糊，还是中值模糊哪个更好？

		cvCvtColor(img,img,CV_RGB2Lab);//转为lab

		cvSplit(img,img_L,img_a,img_b,NULL);//lab图像分配给分通道L,a,b
		cvCopy(img_b,img_b1);  //复制一份b通道，突出蓝黄

		cvCvtColor(img,img,CV_Lab2RGB);

		cvSobel(img_b,img_b_16,1,0);//sobel滤波  x方向1阶导，y方向0阶导   sobel函数要求目标图像深度是16位有符号整型
		cvConvertScale(img_b_16,img_b);

		cvThreshold(img_b,img_b,0,255,CV_THRESH_OTSU+CV_THRESH_BINARY);//二值化

		//处理红绿通道
		cvThreshold(img_a,img_a,T4,255,CV_THRESH_TOZERO_INV);//T4阈值 红变成黑色
		cvThreshold(img_a,img_a,T3,255,CV_THRESH_TOZERO);//T3阈值 绿变成黑色
		cvThreshold(img_a,img_a,T3,255,CV_THRESH_BINARY);//其他为白色

		cvNamedWindow("红绿变黑", CV_WINDOW_AUTOSIZE); //创建窗口
		cvShowImage("红绿变黑", img_a); //显示图像
		//处理黄蓝通道
		cvThreshold(img_b1,img_b1,T1,255,CV_THRESH_TOZERO);//T1蓝色阈值 蓝变成白色
		cvThreshold(img_b1,img_b1,T2,255,CV_THRESH_TOZERO_INV);//T2黄色阈值，黄变白
		cvThreshold(img_b1,img_b1,T1,255,CV_THRESH_BINARY_INV);//其他为白色

 		cvNamedWindow("蓝黄变白", CV_WINDOW_AUTOSIZE); //创建窗口
		cvShowImage("蓝黄变白", img_b1); //显示图像

		cvAnd(img_a,img_b1,img_b1);//b通道去除a通道红绿的干扰 img_b1 = img_a & img_b1

		cvNamedWindow("黄蓝去除红绿干扰", CV_WINDOW_AUTOSIZE); //创建窗口
		cvShowImage("黄蓝去除红绿干扰", img_b1); //显示图像

		cvAnd(img_b,img_b1,img_b);//边缘二值图再与b通道相与 img_b = img_b & img_b1
		cvNamedWindow("边缘二值图再与b通道相与", CV_WINDOW_AUTOSIZE); //创建窗口
		cvShowImage("边缘二值图再与b通道相与", img_b); //显示图像
		IplConvKernel* element=cvCreateStructuringElementEx(17,3,9,2,CV_SHAPE_RECT);//自定义核，17：3的比例，9和2是核的中心坐标与边缘计算
		cvDilate(img_b,img_b,element);//先膨胀
		cvErode(img_b,img_b,element);  //再腐蚀，手动闭操作
//		cvMorphologyEx(img_b,img_b,NULL,element,CV_MOP_CLOSE);//闭操作******************重新写
 		cvNamedWindow("求轮廓图像", CV_WINDOW_AUTOSIZE); //创建窗口
		cvShowImage("求轮廓图像", img_b); //显示图像


		//找轮廓
		CvSeq* contours = NULL;
		CvMemStorage* storage = storage = cvCreateMemStorage(0);
		cvClearMemStorage(storage);
		cvFindContours(img_b,storage,&contours);

//		vector<Plate> Pl;//车牌集合
		if(contours)
			for(CvSeq* c=contours;c!=NULL;c=c->h_next)
			{
				CvBox2D Rect = cvMinAreaRect2(c);
				if( verify(Rect))//排除其他非车牌的轮廓
				{
					Plate p(Rect);
					Point2f vertices[4];
					p.points(vertices);
					for(int i = 0; i<4 ; i++)
					cvLine(img,vertices[i],vertices[(i+1)%4],CvScalar(0,0,255),1); //画矩形的轮廓
					//p.DrawPlate(img);
					//Pl.push_back(p);
				}
			}

		sprintf(OutputFile,"/home/liuyang/Downloads/OPENCV_CODE/input/temp%d.bmp",i);//图像存放目录，测试图片命名test<i>
		cvSaveImage(OutputFile,img);//存结果图片

		cvWaitKey();
		cvDestroyAllWindows();//销毁窗口
		cvReleaseImage(&img);
		cvReleaseImage(&img_L);
		cvReleaseImage(&img_a);
		cvReleaseImage(&img_b);
		cvReleaseImage(&img_b_16);
	}
     return 0;
}
/*PS：第4张车牌有两个LL中间断裂，无法识别，如何将其连上？？？还有就是各种阈值，需要多收集一些图试一试。*/

